﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

using ASCOM.DriverAccess;
using ASCOM.Interface;
using ASCOM.Utilities;

namespace TryASCOM
{
	public class TelescopeEx : Telescope
	{
		#region Events
		public event EventHandler<CoordinatesChangedEventArgs> CoordinatesChanged;
		#endregion

		#region Constants
		private const int SlewUpdateInterval = 700; //700ms
		#endregion

		#region Members
		private BackgroundWorker worker;
		private Coordinate       lastCoordinate;
		#endregion

		#region Properties
		public new bool Connected
		{
			get
			{
				return base.Connected;
			}
			set
			{
				if (base.Connected != value)
				{
					if (base.Connected)
					{
						this.worker.CancelAsync();
						base.Connected = value;
					}
					else
					{
						base.Connected = value;
						this.worker.RunWorkerAsync();
					}
				}
			}
		}
		#endregion

		#region C'Tors and object lifetime code
		public TelescopeEx(string progId) :
			base(progId)
		{
			this.worker = new BackgroundWorker();
			worker.WorkerReportsProgress      = true;
			worker.WorkerSupportsCancellation = true;
			worker.ProgressChanged    += new ProgressChangedEventHandler(Worker_ProgressChanged);
			worker.DoWork             += new DoWorkEventHandler(Worker_DoWork);
			worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
		}
		#endregion

		#region Methods
		protected virtual void OnCoordinatesChanged(Coordinate newCoord)
		{
			if (this.CoordinatesChanged != null)
			{
				CoordinatesChanged(this, new CoordinatesChangedEventArgs(this.lastCoordinate, newCoord));
			}
		}
		#endregion

		#region Event Handlers
		private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			//Pretty much do nothing at all here.
		}

		private void Worker_DoWork(object sender, DoWorkEventArgs e)
		{
			BackgroundWorker worker = sender as BackgroundWorker;

			while (!worker.CancellationPending)
			{
				
					try
					{
						if (base.Slewing)
						{
							Coordinate coord = new Coordinate(
								base.RightAscension,
								base.Declination);

							if (this.lastCoordinate != coord)
							{
								worker.ReportProgress(0, coord);

								this.lastCoordinate = coord;
							}
						}
					}
					catch (Exception)
					{
						//TODO: Handle this
					}
				}

				System.Threading.Thread.Sleep(SlewUpdateInterval);
			

			e.Result = true;
		}

		private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			Coordinate newCoord = (Coordinate)e.UserState;

			OnCoordinatesChanged(newCoord);
		}
		#endregion
	}

	public class CoordinatesChangedEventArgs : EventArgs
	{
		#region Members
		private Coordinate oldCoord;
		private Coordinate newCoord;
		#endregion

		#region Properties
		/// <summary>
		/// Gets the old coordinate value
		/// </summary>
		public Coordinate OldCoordinate
		{
			get
			{
				return this.oldCoord;
			}
		}

		/// <summary>
		/// Gets the new coordinate value
		/// </summary>
		public Coordinate NewCoordinate
		{
			get
			{
				return this.newCoord;
			}
		}

		#endregion

		#region C'Tors and object lifetime code
		public CoordinatesChangedEventArgs(Coordinate oldCoord, Coordinate newCoord) :
			this()
		{
			this.oldCoord = oldCoord;
			this.newCoord = newCoord;
		}

		protected CoordinatesChangedEventArgs()
		{

		}
		#endregion
	}

	public struct Coordinate
	{
		#region Members
		private double ra;
		private double dec;
		#endregion

		#region Properties
		/// <summary>
		/// Gets the Right Ascension of the update event.
		/// </summary>
		public double RightAscension
		{
			get
			{
				return this.ra;
			}
			set
			{
				this.ra = value;
			}
		}

		/// <summary>
		/// Gets the Declination of the update event.
		/// </summary>
		public double Declination
		{
			get
			{
				return this.dec;
			}
			set
			{
				this.dec = value;
			}
		}
		#endregion

		#region C'Tors and object lifetime code
		public Coordinate(double ra, double dec) :
			this()
		{
			this.ra  = ra;
			this.dec = dec;
		}
		#endregion

		#region Methods
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		public static bool operator==(Coordinate coord1, Coordinate coord2)
		{
			return coord1.Equals(coord2);
		}

		public static bool operator!=(Coordinate coord1, Coordinate coord2)
		{
			return !coord1.Equals(coord2);
		}

		public override bool Equals(object obj)
		{
			try
			{
				Coordinate coord = (Coordinate)obj;

				return (this.ra == coord.ra && this.dec == coord.dec);
			}
			catch
			{
				return false;
			}
		}
		#endregion
	}
}
